﻿/********************************************************************
 * * 使本项目源码前请仔细阅读以下协议内容，如果你同意以下协议才能使用本项目所有的功能,
 * * 否则如果你违反了以下协议，有可能陷入法律纠纷和赔偿，作者保留追究法律责任的权利。
 * *
 * * Copyright (C) 2014-? cskin Corporation All rights reserved.
 * * 作者： Amos Li    QQ：443061626   .Net项目技术组群:Amos Li 出品
 * * 请保留以上版权信息，否则作者将保留追究法律责任。
 * * 创建时间：2014-08-05
********************************************************************/
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Configuration;
using HelpCommonLib;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Net.NetworkInformation;

namespace SocketClientCommonLib
{
    /// <summary>
    /// 客户端使用类
    /// </summary>
    public class SyncSocketClient
    {
        /// <summary>
        /// Occurs when the stream is started.
        /// </summary>
        public event NetConnectedEventHandler OnConnected;

        /// <summary>
        /// Occurs when the stream is stopped.
        /// </summary>
        public event NetDisconnectedEventHandler OnDisconnected;

        /// <summary>
        /// Occurs when data received from the stream.
        /// </summary>
        public event NetReceivedEventHandler OnReceived;
        public event NetReceivedBytesEventHandler OnReceivedBytes;

        public event NetMessageEventHandler OnFeedBack;

        protected TcpClient m_tcpClient;
        public event Action<string> MsgEvent;
        /// <summary>
        /// Gets the remote host name.
        /// </summary>
        public string RemoteHost
        {
            get;
            set;
        }

        /// <summary>
        /// Gets the remote port number.
        /// </summary>
        public int RemotePort
        {
            get;
            set;
        }

        /// <summary>
        /// Gets a value indicating whether the client is connected.
        /// </summary>
        public bool IsConnected
        {
            get;
            set;
        }

        /// <summary>
        /// 正常情况下异步断开连接操作
        /// </summary>
        private bool AsynDisConnect = false;


        public bool Done
        {
            get;
            set;
        }

        /// <summary>
        /// 客户端配置
        /// </summary>
        protected SocketUserToken UserToken;


        /// <summary>
        /// 发送信息时组装信息协议(信息组装后发送客户端)
        /// </summary>
        private AssemblyOutDataParser m_outDataParser;
        public AssemblyOutDataParser OutDataParser { get { return m_outDataParser; } set { m_outDataParser = value; } }

        /// <summary>
        /// 接收时候信息组装信息(确定信息是否合格)
        /// </summary>
        private AssemblyInDataParser m_InDataParser;
        public AssemblyInDataParser InDataParser { get { return m_InDataParser; } set { m_InDataParser = value; } }

        #region 断线重连变量
        private DateTime lstHeartbeatTime = DateTime.Now;
        public int ReconnectionTime = 120;
        public bool IsReconnect = true;
        //public bool IsReconnect = ConfigurationManager.AppSettings.Get("IsReconnect") == "On"?true:false;
        private Timer heartbeatTimer;
        private TimerCallback callback;
        private string heartbeatSignal = ConfigurationManager.AppSettings.Get("HeartbeatSignal");
        private AutoResetEvent mEvent = new AutoResetEvent(false);



        #endregion

        CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
        /// <summary>
        /// 连接服务器
        /// </summary>
        /// <param name="host">IP</param>
        /// <param name="port">地址</param>
        public void Connect(string Serverip, int Port, bool isStart = true)
        {
            try
            {
                if (isStart)
                {
                    AsynDisConnect = false;
                    StartHeartbeatTimer();
                }


                m_outDataParser = new AssemblyOutDataParser();
                m_InDataParser = new AssemblyInDataParser();
                RemoteHost = Serverip;
                RemotePort = Port;

                m_tcpClient = new TcpClient();
                m_tcpClient.Client.Blocking = true;
                //m_tcpClient.Client.Bind(new IPEndPoint(IPAddress.Parse(Serverip), Port));
                try
                {

                    m_tcpClient.Connect(Serverip, Port);


                    ////无数据交互持续时间(ms)
                    ////发送探测包间隔(ms)
                    //uint _keepAliveTime = 5000;
                    //uint _keepAliveInterval = 1000;
                    ////设置KeepAlive
                    //m_tcpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
                    //byte[] optionValue = new byte[12];
                    //BitConverter.GetBytes(1).CopyTo(optionValue, 0);
                    //BitConverter.GetBytes(_keepAliveTime).CopyTo(optionValue, 4);
                    //BitConverter.GetBytes(_keepAliveInterval).CopyTo(optionValue, 8);
                    //m_tcpClient.Client.IOControl(IOControlCode.KeepAliveValues, optionValue, null);

                }
                catch (SocketException ex)
                {
                    string errMsg = string.Format("客户端{0}:{1}无法连接到服务器,请检查网络状况！", Serverip, Port);

                    if (OnFeedBack != null)
                        OnFeedBack(this, new NetMessageEventArgs(errMsg, ex));
                    return;
                }
                IsConnected = true;

                if (OnConnected != null)
                    OnConnected(this, null);
                m_tcpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);


                NetworkStream nStream = new NetworkStream(m_tcpClient.Client, true);
                UserToken = new SocketUserToken(Serverip, Port, m_tcpClient, nStream);
                //UserToken.m_nStream.BeginRead(UserToken.ReceiveBuffer.Buffer, 0, UserToken.ReceiveBuffer.Buffer.Length, new AsyncCallback(EndReader), UserToken);
                if (UserToken.m_nStream != null)
                {
                    if (AsynDisConnect || m_tcpClient == null || m_tcpClient.Client == null || !CommonClass.IsSocketConnected(m_tcpClient.Client))
                    {
                        if (AsynDisConnect)
                        {
                            Logger.Error("AsynDisConnect");
                        }
                        if (m_tcpClient == null)
                        {
                            Logger.Error("m_tcpClient");
                        }
                        if (m_tcpClient.Client == null)
                        {
                            Logger.Error("m_tcpClient.Client");
                        }
                        if (!CommonClass.IsSocketConnected(m_tcpClient.Client))
                        {
                            Logger.Error("!IsSocketConnected" + m_tcpClient.Client.Connected.ToString());
                            m_tcpClient.Client.Shutdown(SocketShutdown.Both);
                            m_tcpClient.Close();
                            m_tcpClient = null;
                            m_tcpClient = new TcpClient();
                            m_tcpClient.Client.Connect(Serverip, Port);
                            UserToken = null;
                            nStream = new NetworkStream(m_tcpClient.Client, true);
                            UserToken = new SocketUserToken(Serverip, Port, m_tcpClient, nStream);
                            if (!CommonClass.IsSocketConnected(m_tcpClient.Client))
                            {
                                Logger.Error("!IsSocketConnected1");
                                AsynDisConnect = false;
                                UserToken.m_nStream.Close();
                                UserToken.m_nStream.Dispose();
                                //mEvent.Set();
                                Logger.Error("==-----12312312-----===");
                                return;
                            }
                            else
                            {
                                Logger.Error("==-----5555555555555-----===");
                                UserToken.m_nStream.BeginRead(UserToken.ReceiveBuffer.Buffer, 0, UserToken.ReceiveBuffer.Buffer.Length, new AsyncCallback(EndReader), UserToken);


                            }
                        }

                    }
                    else
                    {
                        Logger.Error("==-----4444444-----===");
                        UserToken.m_nStream.BeginRead(UserToken.ReceiveBuffer.Buffer, 0, UserToken.ReceiveBuffer.Buffer.Length, new AsyncCallback(EndReader), UserToken);
                    }
                }
                else
                {
                    Logger.Error("==-----33333333333-----===");
                }
            }
            catch (Exception ex)
            {
                string errMsg = string.Format("客户端{0}:{1}连接异常！", Serverip, Port);
                if (OnFeedBack != null)
                    OnFeedBack(this, new NetMessageEventArgs(errMsg, ex));
            }
        }

        /// <summary>
        /// 异步接收回调函数
        /// </summary>
        /// <param name="ir"></param>
        private void EndReader(IAsyncResult ir)
        {
            SocketUserToken ir_UserToken = ir.AsyncState as SocketUserToken;

            try
            {
                if (ir_UserToken.m_nStream != null)
                {
                    //结束通信时关闭网络流,条件1,2,3为手动或自动正常结束通信判断,条件4为异常结束通信判断
                    //通过条件1判断正常状况断开连接,防止在判断条件3和条件4的过程中断开连接操作并发执行m_tcpClient.Client = null
                    if (AsynDisConnect || m_tcpClient == null || m_tcpClient.Client == null || !CommonClass.IsSocketConnected(m_tcpClient.Client))
                    {
                        AsynDisConnect = false;
                        ir_UserToken.m_nStream.Close();
                        ir_UserToken.m_nStream.Dispose();
                        mEvent.Set();
                        return;
                    }
                    ir_UserToken.Offset = ir_UserToken.m_nStream.EndRead(ir);
                    byte[] buffer = new byte[ir_UserToken.Offset];
                    Array.Copy(ir_UserToken.ReceiveBuffer.Buffer, buffer, ir_UserToken.Offset);
                    string tmpStr = Encoding.UTF8.GetString(buffer, 0, ir_UserToken.Offset);
                    if (!m_InDataParser.DecodeProtocolText(tmpStr))//组装成功则进入
                    {
                        ir_UserToken.ReceiveBuffer.Clear(ir_UserToken.Offset);//接收完成后清除掉前面的缓存

                        if (OnReceivedBytes != null)
                            OnReceivedBytes(this, new NetReceivedBytesEventArgs(buffer));

                        if (OnReceived != null)
                        {
                            string data = Encoding.Default.GetString(buffer);
                            OnReceived(this, new NetReceivedEventArgs(data));
                        }
                        //组装接收的协议
                        //DelegateState.ServerStateInfo("信息" + Encoding.UTF8.GetString(buffer));//处理信息
                    }
                    UserToken.m_nStream.BeginRead(UserToken.ReceiveBuffer.Buffer, 0, UserToken.ReceiveBuffer.Buffer.Length, new AsyncCallback(EndReader), UserToken);
                }
            }
            catch (Exception ex)
            {
                if (OnFeedBack != null)
                    OnFeedBack(this, new NetMessageEventArgs("客户端接收数据异常！", ex));
            }
        }

        /// <summary>
        /// 关闭客户端
        /// </summary>
        public void Disconnect()
        {
            try
            {
                StopHeartbeatTimer();

                if (!IsConnected)
                    return;

                AsynDisConnect = true;

                if (m_tcpClient != null && m_tcpClient.Client != null)
                {
                    m_tcpClient.Client.Shutdown(SocketShutdown.Both);
                    m_tcpClient.Close();//执行完此操作后,会执行异步接收回调函数
                    m_tcpClient = null;
                }

                IsConnected = false;

                if (OnDisconnected != null)
                    OnDisconnected(this, null);
            }
            catch (Exception ex)
            {
                string errMsg = string.Format("客户端{0}:{1}断开连接异常！", this.RemoteHost, this.RemotePort);
                if (OnFeedBack != null)
                    OnFeedBack(this, new NetMessageEventArgs(errMsg, ex));
            }
        }

        /// <summary>
        /// 组包发送
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="offset"></param>
        /// <param name="count"></param>
        public bool SendCommand()
        {
            //OutDataParser.GetProtocolText()结合使用 下
            OutDataParser.Clear();
            OutDataParser.AddResponse();
            OutDataParser.AddValue(ProtocolKeys.UserName, "admin");
            OutDataParser.AddValue(ProtocolKeys.Password, HelpCommonLib.CommonClass.MD5Encrypt("admin", 16));

            string commandText = OutDataParser.GetProtocolText();
            byte[] bufferUTF8 = Encoding.Default.GetBytes(commandText);
            int totalLength = bufferUTF8.Length; //获取总大小
            UserToken.SendBuffer.StartPacket();
            UserToken.SendBuffer.m_dynamicBufferManager.WriteInt(totalLength); //写入总大小
            UserToken.SendBuffer.m_dynamicBufferManager.WriteBuffer(bufferUTF8); //写入命令内容
            UserToken.SendBuffer.EndPacket();

            int packetOffset = 0;
            int packetCount = 0;
            if (UserToken.SendBuffer.GetFirstPacket(ref packetOffset, ref packetCount))
            {
                m_tcpClient.Client.Send(UserToken.SendBuffer.m_dynamicBufferManager.Buffer, 0, UserToken.SendBuffer.m_dynamicBufferManager.DataCount, SocketFlags.None); //使用阻塞模式，Socket会一次发送完所有数据后才返回
                while (UserToken.SendBuffer.GetFirstPacket(ref packetOffset, ref packetCount))
                {
                    UserToken.SendBuffer.ClearFirstPacket();
                    m_tcpClient.Client.Send(UserToken.SendBuffer.m_dynamicBufferManager.Buffer, 0, UserToken.SendBuffer.m_dynamicBufferManager.DataCount, SocketFlags.None); //使用阻塞模式，Socket会一次发送完所有数据后才返回    
                }
            }
            return true;
        }

        /// <summary>
        /// 发送信息
        /// </summary>
        /// <param name="commandText">信息</param>
        /// <returns>bool</returns>
        public bool SendCommand(string commandText)
        {

            byte[] bufferUTF8 = Encoding.Default.GetBytes(commandText);
            int totalLength = sizeof(int) + bufferUTF8.Length; //获取总大小
            UserToken.SendBuffer.StartPacket();
            //UserToken.SendBuffer.m_dynamicBufferManager.WriteInt(totalLength); //写入总大小
            UserToken.SendBuffer.m_dynamicBufferManager.WriteBuffer(bufferUTF8); //写入命令内容
            UserToken.SendBuffer.EndPacket();

            int packetOffset = 0;
            int packetCount = 0;
            if (UserToken.SendBuffer.GetFirstPacket(ref packetOffset, ref packetCount))
            {
                m_tcpClient.Client.Send(UserToken.SendBuffer.m_dynamicBufferManager.Buffer, 0, UserToken.SendBuffer.m_dynamicBufferManager.DataCount, SocketFlags.None); //使用阻塞模式，Socket会一次发送完所有数据后才返回
                while (UserToken.SendBuffer.GetFirstPacket(ref packetOffset, ref packetCount))
                {
                    UserToken.SendBuffer.ClearFirstPacket();
                    m_tcpClient.Client.Send(UserToken.SendBuffer.m_dynamicBufferManager.Buffer, 0, UserToken.SendBuffer.m_dynamicBufferManager.DataCount, SocketFlags.None); //使用阻塞模式，Socket会一次发送完所有数据后才返回    
                }
            }
            return true;
        }

        #region 断线重连方法

        public void UpdateHeartbeat()
        {
            lstHeartbeatTime = DateTime.Now;
        }


        private void StartHeartbeatTimer()
        {
            
                cancellationTokenSource = new CancellationTokenSource();

                Task.Factory.StartNew(() => {

                    while (!cancellationTokenSource.IsCancellationRequested)
                    {

        
                        if (this.IsConnected)
                        {
                            this.HeartbeatCheck();
                        }

                        Thread.Sleep(1000);

                    }


                });

                //    if (heartbeatTimer == null)
                //    {
                //        callback = new TimerCallback(HeartbeatCheck);
                //        heartbeatTimer = new Timer(callback, null, TimeSpan.FromSeconds(ReconnectionTime), TimeSpan.FromSeconds(ReconnectionTime));
                //    }
                //    else
                //    {
                //        heartbeatTimer.Change(TimeSpan.FromSeconds(ReconnectionTime), TimeSpan.FromSeconds(ReconnectionTime));
                //    }
           
        }

        private void StopHeartbeatTimer()
        {
            //if (IsReconnect)
            //{
            //    if (heartbeatTimer != null)
            //    {
            //        heartbeatTimer.Change(Timeout.Infinite, Timeout.Infinite);
            //    }
            //}
            if (cancellationTokenSource != null)
            {
                cancellationTokenSource.Cancel();
            }
        }

        private void HeartbeatCheck()
        {
            if (DateTime.Now.Subtract(lstHeartbeatTime).TotalSeconds >= ReconnectionTime)
            {

                if (this.m_tcpClient != null && !this.m_tcpClient.Connected)
                {
                 
                    if (this.IsReconnect)
                    {
                        string msg = "连接已断开,正在尝试重连！";
                        if (MsgEvent != null)
                        {
                            MsgEvent(msg);
                        }
                        this.ReConnect();
                    }
                    else {
                        string msg = "无法连接到服务器,请检查网络状况！";
                        if (OnFeedBack != null)
                            OnFeedBack(this, new NetMessageEventArgs(msg, new Exception(msg)));
                    }
                   
                    Thread.Sleep(5000);
                }
                else if (this.IsConnected)
                {
                    try
                    {
                        if (!this.HostPing(RemoteHost))
                        {
                            string msg = "无法连接到服务器,请检查网络状况！";
                            if (OnFeedBack != null)
                                OnFeedBack(this, new NetMessageEventArgs(msg, new Exception(msg)));
                            Thread.Sleep(5000);
                            if (this.HostPing(RemoteHost))
                            {
                                if (MsgEvent != null)
                                {
                                    MsgEvent($"目标服务器恢复响应，客户端进行断线重连");
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.ToString());
                    }
                }

            }
        }


        /// <summary>
        /// 断线重连时，一定要先完全断开之后再重新连接
        /// </summary>
        public void ReConnect()
        {

            /*
             * 必须等待资源释放
             * 1.Socket资源释放,2.网络流释放
             * 才可以重新连接
             */
            //mEvent.WaitOne(1000, false);
            this.Connect(this.RemoteHost, this.RemotePort, false);
       
        }

        private bool HostPing(string hostIp)
        {
            Ping ping = new Ping();
            int timeout = 1000;
            PingReply reply = ping.Send(hostIp, timeout);
            if (reply.Status != IPStatus.Success)
                return false;
            else return true;
        }

        #endregion


    }
}